package com.enation.app.javashop.service.member.impl;


import cn.hutool.core.lang.UUID;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.enation.app.javashop.framework.auth.Token;
import com.enation.app.javashop.framework.cache.Cache;
import com.enation.app.javashop.framework.context.request.ThreadContextHolder;
import com.enation.app.javashop.framework.exception.ServiceException;
import com.enation.app.javashop.framework.logs.Logger;
import com.enation.app.javashop.framework.logs.LoggerFactory;
import com.enation.app.javashop.framework.rabbitmq.MessageSender;
import com.enation.app.javashop.framework.rabbitmq.MqMessage;
import com.enation.app.javashop.framework.security.TokenManager;
import com.enation.app.javashop.framework.security.model.Buyer;
import com.enation.app.javashop.framework.util.HttpUtils;
import com.enation.app.javashop.framework.util.StringUtil;
import com.enation.app.javashop.mapper.member.ConnectMapper;
import com.enation.app.javashop.mapper.member.MemberMapper;
import com.enation.app.javashop.model.base.CachePrefix;
import com.enation.app.javashop.model.base.message.MemberRegisterMsg;
import com.enation.app.javashop.model.base.rabbitmq.AmqpExchange;
import com.enation.app.javashop.model.errorcode.MemberErrorCode;
import com.enation.app.javashop.model.member.dos.ConnectDO;
import com.enation.app.javashop.model.member.dos.Member;
import com.enation.app.javashop.model.member.dto.QQUserDTO;
import com.enation.app.javashop.model.member.dto.WeChatUserDTO;
import com.enation.app.javashop.model.member.enums.ConnectTypeEnum;
import com.enation.app.javashop.model.member.vo.Auth2Token;
import com.enation.app.javashop.model.member.vo.MemberLoginMsg;
import com.enation.app.javashop.model.member.vo.MemberVO;
import com.enation.app.javashop.service.member.AbstractConnectLoginPlugin;
import com.enation.app.javashop.service.member.ConnectManager;
import com.enation.app.javashop.service.member.LoginQQManager;
import com.enation.app.javashop.service.member.MemberManager;
import com.enation.app.javashop.service.member.plugin.qq.QQConnectLoginPlugin;
import com.enation.app.javashop.service.statistics.util.DateUtil;
import net.sf.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;


/**
 * QQ统一登陆服务实现
 * @author cs
 * @since v1.0
 * @version 7.2.2
 * 2020/09/24
 */
@Service
public class LoginQQManagerImpl implements LoginQQManager {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private ConnectMapper connectMapper;

    @Autowired
    private Cache cache;

    @Autowired
    private MemberMapper memberMapper;

    @Autowired
    private MemberManager memberManager;

    @Autowired
    private TokenManager tokenManager;

    @Autowired
    private MessageSender messageSender;
    @Autowired
    private ConnectManager connectManager;

    private static final Integer QQ_TOKEN_VAILD_TIME_APP = 60*60*24*90;


    @Override
    public Map qqWapLogin(String accessToken, String uuid) {
        JSONObject jsonObject = getUnionId(accessToken);
        String unionid = jsonObject.getString("unionid");
        String openid = jsonObject.getString("openid");
        QQUserDTO userDTO = getQQUserInfo(accessToken, openid);
        userDTO.setUnionid(unionid);
        userDTO.setOpenid(openid);
        Map map = loginByUnionId(uuid, userDTO, null, null, ConnectTypeEnum.QQ_OPENID);
        return map;
    }

    /**
     * 根据UnionId登陆
     * @param
     * @return
     */
    private Map loginByUnionId(String uuid, QQUserDTO userDTO, Integer tokenOutTime, Integer refreshTokenOutTime, ConnectTypeEnum loginType){
        Map res = new HashMap(16);
        //通过unionid查找会员(es_connect表)
        ConnectDO connectDO= findMemberByUnionId(userDTO.getUnionid());
        Member member = null;
        if(connectDO==null){
            //没找到注册一个
            member = registerBy(userDTO,loginType);
        }else{
            member=findMemberById(connectDO.getMemberId(),userDTO.getOpenid(),loginType);
        }
        //存储uuid和unionId的关系
        Auth2Token auth2Token = new Auth2Token();
        auth2Token.setType(loginType.value());
        auth2Token.setUnionid(userDTO.getUnionid());
        //openid用于注册绑定时获取登录的微信的信息
        auth2Token.setOpneId(userDTO.getOpenid());
        logger.debug("微信登录openId为：" + userDTO.getOpenid());
        cache.put(CachePrefix.CONNECT_LOGIN.getPrefix() + uuid, auth2Token);
        MemberVO memberVO = this.connectWeChatLoginHandle(member, uuid,tokenOutTime,refreshTokenOutTime);
        res.put("access_token", memberVO.getAccessToken());
        res.put("refresh_token", memberVO.getRefreshToken());
        res.put("uid", memberVO.getUid());
        return res;
    }

    /**
     * 生成member的token
     *
     * @param member
     * @param uuid
     * @return
     */
    private MemberVO convertWechatMember(Member member, String uuid,Integer tokenOutTime,Integer refreshTokenOutTime) {
        //校验当前账号是否被禁用
        if (!member.getDisabled().equals(0)) {
            throw new ServiceException(MemberErrorCode.E107.code(), "当前账号已经禁用，请联系管理员");
        }

        //新建买家用户角色对象
        Buyer buyer = new Buyer();
        //设置用户ID
        buyer.setUid(member.getMemberId());
        //设置用户名称
        buyer.setUsername(member.getUname());
        //设置uuid
        buyer.setUuid(uuid);
        //创建Token
        Token token = tokenManager.create(buyer,tokenOutTime,refreshTokenOutTime);
        //获取访问Token
        String accessToken = token.getAccessToken();
        //获取刷新Token
        String refreshToken = token.getRefreshToken();
        //组织返回数据
        MemberVO memberVO = new MemberVO(member, accessToken, refreshToken);
        return memberVO;
    }

    public MemberVO connectWeChatLoginHandle(Member member, String uuid,Integer tokenOutTime,Integer refreshTokenOutTime) {
        //初始化会员信息
        MemberVO memberVO = this.convertWechatMember(member, uuid,tokenOutTime,refreshTokenOutTime);
        //发送登录消息
        this.sendMessage(member, 1);
        return memberVO;
    }
    /**
     * 发送登录消息
     *
     * @param member
     * @param memberOrSeller
     */
    private void sendMessage(Member member, Integer memberOrSeller) {
        MemberLoginMsg loginMsg = new MemberLoginMsg();
        loginMsg.setLastLoginTime(member.getLastLogin());
        loginMsg.setMemberId(member.getMemberId());
        loginMsg.setMemberOrSeller(memberOrSeller);
        this.messageSender.send(new MqMessage(AmqpExchange.MEMEBER_LOGIN, AmqpExchange.MEMEBER_LOGIN + "_ROUTING", loginMsg));
    }


    /**
     * 根据用户id查询用户信息
     * @param memberId
     * @param openid
     * @param loginType
     * @return
     */
    private Member findMemberById(Long memberId, String openid, ConnectTypeEnum loginType) {
        Member member = memberManager.getModel(memberId);
        //查看当前登陆终端该账户openid是否已记录，如果未记录则新增记录
        ConnectDO aDo = connectMapper.selectOne(new QueryWrapper<ConnectDO>().eq("member_id", member.getMemberId()).eq("union_type", loginType.value()));
        if (aDo==null){
            ConnectDO connect = new ConnectDO();
            connect.setMemberId(member.getMemberId());
            connect.setUnionId(openid);
            connect.setUnionType(loginType.value());
            connectMapper.insert(connect);
        }
        return member;
    }

    /**
     * 注册
     * @param userDTO
     * @return
     */
    private Member  registerBy(QQUserDTO userDTO,ConnectTypeEnum loginType) {
        String usernamePrefix = "m_";
        String usernameSuffix = UUID.fastUUID().toString(true).substring(0,9);
        String username = usernamePrefix+usernameSuffix;
        Member memberByName = memberManager.getMemberByName(username);
        while (memberByName!=null){
            usernameSuffix = UUID.fastUUID().toString(true).substring(0,9);
            username = usernamePrefix+usernameSuffix;
            memberByName = memberManager.getMemberByName(username);
        }
        Member member = new Member();
        member.setUname(username);
        long dateline = DateUtil.getDateline();
        member.setCreateTime(dateline);
        member.setRegisterIp(ThreadContextHolder.getHttpRequest().getRemoteAddr());
        member.setLoginCount(1);
        member.setLastLogin(dateline);
        member.setDisabled(0);
        member.setNickname(StringUtil.isEmpty(userDTO.getNickname())?username:userDTO.getNickname());
        member.setCity(userDTO.getCity());
        member.setProvince(userDTO.getProvince());
        if ("男".equals(userDTO.getGender())){
            member.setSex(1);
        }else{
            member.setSex(0);
        }
        //设置会员等级积分为0
        member.setGradePoint(0L);
        //设置会员消费积分为0
        member.setConsumPoint(0L);
        //设置会员是否开启店铺 0：否，1：是
        member.setHaveShop(0);
        //设置会员是否完善了个人信息 0：否，1：是
        member.setInfoFull(0);
        memberMapper.insert(member);
        //组织数据结构发送会员注册消息
        MemberRegisterMsg memberRegisterMsg = new MemberRegisterMsg();
        memberRegisterMsg.setMember(member);
        memberRegisterMsg.setUuid(ThreadContextHolder.getHttpRequest().getHeader("uuid"));
        this.messageSender.send(new MqMessage(AmqpExchange.MEMEBER_REGISTER, AmqpExchange.MEMEBER_REGISTER + "_ROUTING", memberRegisterMsg));
        addConnect(userDTO, member,loginType);
        return member;
    }

    private void addConnect(QQUserDTO userDTO, Member member, ConnectTypeEnum loginType) {
        //写入UnionId
        ConnectDO connect = new ConnectDO();
        connect.setMemberId(member.getMemberId());
        connect.setUnionType(ConnectTypeEnum.QQ.value());
        connect.setUnionId(userDTO.getUnionid());
        connectMapper.insert(connect);
        //写入openId
        connect = new ConnectDO();
        connect.setMemberId(member.getMemberId());
        connect.setUnionType(loginType.value());
        connect.setUnionId(userDTO.getOpenid());
        connectMapper.insert(connect);
    }

    private QQUserDTO getQQUserInfo(String accessToken,String openid){
        Map<String, String> map = connectManager.initConnectSetting();
        StringBuffer userBuffer = new StringBuffer("https://graph.qq.com/user/get_user_info?");
        userBuffer.append("access_token=").append(accessToken);
        userBuffer.append("&openid=").append(openid);
        userBuffer.append("&oauth_consumer_key=").append(map.get("qq_pc_app_id"));
        userBuffer.append("&format=json");
        String retJson = HttpUtils.doGet(userBuffer.toString(), "UTF-8", 100, 1000);
        JSONObject jsonObject = JSONObject.fromObject(retJson);
        if (jsonObject.getInt("ret")!=0){
            throw new ServiceException("403","获取用户信息失败",retJson);
        }
        return toDTO(jsonObject);
    }

    private QQUserDTO toDTO(JSONObject jsonObject) {
        QQUserDTO userDTO = new QQUserDTO();
        userDTO.setHeadimgurl(jsonObject.getString("figureurl_qq"));
        userDTO.setNickname(jsonObject.getString("nickname"));
        userDTO.setCity(jsonObject.getString("city"));
        userDTO.setProvince(jsonObject.getString("province"));
        userDTO.setGender(jsonObject.getString("gender"));
        return userDTO;
    }

    private JSONObject getUnionId(String accessToken){
        StringBuffer unionIdBuffer = new StringBuffer("https://graph.qq.com/oauth2.0/me?");
        unionIdBuffer.append("access_token=").append(accessToken);
        unionIdBuffer.append("&unionid=1&fmt=json");
        String retJson = HttpUtils.doGet(unionIdBuffer.toString(), "UTF-8", 100, 1000);
        if (retJson.indexOf("unionid")==-1){
            throw new ServiceException("403","fail to get unionid",retJson);
        }
        return JSONObject.fromObject(retJson);
    }

    @Override
    public Map qqAppLogin(String uuid, QQUserDTO qqUserDTO) {
        JSONObject jsonObject = getUnionId(qqUserDTO.getAccesstoken());
        String unionid = jsonObject.getString("unionid");
        qqUserDTO.setUnionid(unionid);
        return loginByUnionId(uuid,qqUserDTO,QQ_TOKEN_VAILD_TIME_APP,QQ_TOKEN_VAILD_TIME_APP,ConnectTypeEnum.QQ_APP);
    }

    /**
     * 根据unionId查询connect
     * @param unionId
     * @return
     */
    private ConnectDO findMemberByUnionId(String unionId) {
        return connectMapper.selectOne(new QueryWrapper<ConnectDO>().eq("union_id",unionId).eq("union_type", ConnectTypeEnum.QQ));
    }
}
